import os
import re
import sys
import config
import pandas as pd
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor
from pptx.enum.shapes import MSO_SHAPE
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
from utils import create_hexagon,create_circle_tree

os.makedirs('temp', exist_ok=True)


fun_caller_dict = {
    '名词解释': 'fun_caller_name_explain', 
    '时间线': 'fun_caller_time_line', 
    '三级主题排版1': 'fun_caller_three_level_t1', 
    '要点': 'fun_caller_point_list', 
    '要点六边形': 'fun_caller_point_hexagon', 
    '链接': 'fun_caller_urls',
    '截图': 'fun_caller_screen_cut',
}

# ------------------------------------------------------------------------------- 其他函数...

def hex_to_rgbcolor(hex_color):
    hex_color = hex_color.lstrip('#').upper()
    r, g, b = int(hex_color[0:2], 16), int(hex_color[2:4], 16), int(hex_color[4:6], 16)
    return RGBColor(r, g, b)

def blend_with_white(rgb_color, opacity):
    """
    将颜色与白色混合，模拟透明度效果。
    
    :param rgb_color: 原始颜色，为 RGBColor 对象。
    :param opacity: 透明度，范围从 0.0 (完全透明) 到 1.0 (完全不透明)。
    :return: 混合后的颜色，为 RGBColor 对象。
    """
    # 白色背景
    (r,g,b) = rgb_color
    [r1,g1,b1]= [255, 255, 255]
    
    # 混合颜色
    r = int(r * opacity + r1 * (1 - opacity))
    g = int(g * opacity + g1 * (1 - opacity))
    b = int(b * opacity + b1 * (1 - opacity))
    return RGBColor(r, g, b)

def calculate_text_width(text_str, font_size=24, chinese_coefficient=1.2, english_coefficient=0.7):
    chinese_chars = re.findall(r'[\u4e00-\u9fff]', text_str)
    english_chars = re.findall(r'[a-zA-Z]', text_str)
    # 计算宽度
    width = (len(chinese_chars) * chinese_coefficient + len(english_chars) * english_coefficient) * font_size
    # 转换为英寸
    width_in_inches = Inches(width / 72)  # 假设72pt=1英寸
    return width_in_inches

def split_text_by_keywords(text, keywords):
    # 对关键词按长度降序排序，以优先匹配最长的关键词
    keywords = sorted(keywords, key=len, reverse=True)
    result = []
    i = 0
    while i < len(text):
        # print(i, len(text))
        matched = False
        for keyword in keywords:
            # 如果当前位置开始的字符串包含关键词，则添加到结果中
            if text[i:].startswith(keyword):
                result.append({'w': keyword, 'type': 'key'})
                i += len(keyword)
                matched = True
                break
        # 如果当前位置的字符串不是任何一个关键词的开头，则将其视为非关键词
        if not matched:
            if result and result[-1]['type'] == 'non-key':
                # 如果上一个元素也是非关键词，合并它们
                result[-1]['w'] += text[i]
            else:
                # 否则，创建一个新的非关键词元素
                result.append({'w': text[i], 'type': 'non-key'})
            i += 1
    return result

def slide_add_text(prs_info,slide,left=0.01,top=0.01, text_str='test插入标题',keywords_str='', font_size=24, font_color=RGBColor(0, 0, 0), opacity=1,
    is_fill=True,fill_color=None,box_height=None,box_width=None,font_bold=False,word_wrap=True,align_center=False):
    # print('prs_info.........................', prs_info)
    left  = int(left*prs_info['prs_width'])
    top = int(top*prs_info['prs_height'])

    # --------------------------增加文本框
    if box_width is None:
        width = min(calculate_text_width(text_str, font_size), prs_info['prs_width']*0.9)
    else:
        width = prs_info['prs_width']*box_width
    if box_height is None:
        height = Inches(0.8*font_size/40)
    else:
        height = prs_info['prs_height']*box_height
    txBox = slide.shapes.add_textbox(left, top, width, height)
    tf = txBox.text_frame

    # 确保至少有一个段落
    # if not tf.paragraphs:
    tf.clear()  # 清除任何现有的段落
    p = tf.add_paragraph()
    if keywords_str!='':
        keywords = keywords_str.replace('、', '|').replace(' ', '').replace(',', '|').replace('，', '|').split('|')
        # print('keywords...'*10, keywords)
        result = split_text_by_keywords(text_str, keywords)
        # for i in result:print(i)
        i = 0
        for ind,d in enumerate(result):
            run = p.add_run()  # 为每个单词创建一个新的运行
            run.text = d['w']
            if d['type']=='key':
                run.font.color.rgb = prs_info['theme_color'][i%len(prs_info['theme_color'])]
                i += 1
                run.font.bold = True
            else:
                run.font.color.rgb = font_color
            run.font.size = Pt(font_size)
    else:
        # 直接修改第一个段落的内容和样式
        p = tf.paragraphs[0]
        p.text = text_str
        # 设置字体大小、颜色和加粗
        run = p.runs[0]
        run.font.size = Pt(font_size)
        run.font.color.rgb = font_color
        run.font.bold = font_bold
    if align_center:
        p.alignment = PP_ALIGN.CENTER
        tf.text_anchor = MSO_ANCHOR.MIDDLE
    # 设置文本框背景颜色和透明度
    if is_fill:
        
        fill = txBox.fill
        fill.solid()
        if fill_color is None:
            fill.fore_color.rgb = prs_info['fill_color']
        else:
            fill.fore_color.rgb = blend_with_white(fill_color,opacity)
        # fill.fore_color.transparency = opacity  # 透明度范围是 0.0（完全不透明）到 1.0（完全透明）
    tf.word_wrap = word_wrap  # 确保启用自动换行

def slide_add_table(prs_info, slide, df, column_widths=None, left=0.02, top=0.12, default_width=1.0):
    rows, cols = df.shape
    left  = int(left*prs_info['prs_width'])
    top = int(top*prs_info['prs_height'])
    total_width = int(prs_info['prs_width']*0.9)
    
    # 如果没有提供列宽字典或字典为空，则所有列使用默认宽度
    if not column_widths:
        column_widths = {}

    # 创建表格
    table_widths = [column_widths.get(x, default_width) for x in df.columns]
    sum_w = sum(table_widths)
    table_widths = [int(total_width*x/sum_w) for x in table_widths]
    table = slide.shapes.add_table(rows + 1, cols, left, top, total_width, int(prs_info['prs_height']*0.8)).table

    # 设置列宽
    for i, width in enumerate(table_widths):
        table.columns[i].width = width

    # 添加DataFrame的列名到表格的第一行
    for i, column in enumerate(df.columns):
        table.cell(0, i).text = column

    # 添加DataFrame的数据到表格中
    for i in range(rows):
        for j in range(cols):
            table.cell(i + 1, j).text = str(df.iloc[i, j])

    return slide
# ------------------------------------------------------------------------------- pptx_type_caller.py
def fun_caller_name_explain(slide, df, prs_info):
    # print('名词解释', '*'*50)
    # print(df.columns)
    # all_str_len = len(''.join(df['解释']))
    df['len'] = df['解释'].apply(len)
    total_length = df['len'].sum()
    df['len_ratio'] = df['len'] / total_length
    font_size = min(40-int(total_length**0.5), 24)
    top = 0.1
    for i,r in df.iterrows():
        slide_add_text(prs_info,slide,left=0.01,top=top, text_str=r['解释'],keywords_str=r['关键词'], font_size=font_size, font_color=RGBColor(0, 0, 0), opacity=0,is_fill=False)
        top += 0.9*r['len_ratio']
    # print('\n\nprs_info')
    # print(prs_info)
    # 对名词解释slide进行处理的代码
    pass

def fun_caller_time_line(slide, df, prs_info):
    df = df.sort_values('时间').reset_index(drop=True)
    df = df.astype(str)
    df['len'] = df['事件'].apply(len)
    total_length = df['len'].sum()
    df['len_ratio'] = df['len'] / total_length

    # 添加一个箭头
    # shape = slide.shapes.add_shape(MSO_SHAPE.RIGHT_ARROW, left=prs_info['prs_width']*0.02, top=prs_info['prs_height']*0.20, width=prs_info['prs_width']*0.9, height=prs_info['prs_height']*0.05)

    font_size = min(38-int(total_length**0.5), 24)
    # print('font_size...'*5, font_size)
    left = 0.0
    for i,r in df.iterrows():
        slide_add_text(prs_info,slide,left=left,top=0.25, text_str=r['事件'], font_size=font_size, font_color=RGBColor(0, 0, 0), opacity=0.3,is_fill=True,box_width=r['len_ratio'],fill_color=prs_info['theme_color'][i%len(prs_info['theme_color'])],box_height=0.65)
        # 日期
        slide_add_text(prs_info,slide,left=left,top=0.15, text_str=r['时间'], font_size=font_size, font_color=RGBColor(0, 0, 0), is_fill=True,box_width=r['len_ratio'],font_bold=True,fill_color=prs_info['theme_color'][i%len(prs_info['theme_color'])],align_center=True,box_height=0.1)
        # 主体
        slide_add_text(prs_info,slide,left=left,top=0.9, text_str=r['主体'], font_size=font_size, font_color=RGBColor(0, 0, 0), is_fill=False,box_width=r['len_ratio'],font_bold=True,fill_color=prs_info['theme_color'][i%len(prs_info['theme_color'])],align_center=True)
        left += r['len_ratio']

def fun_caller_three_level_t1(slide, df, prs_info):
    # 对时间线slide进行处理的代码
    p1 = 'temp/test.png'
    create_circle_tree.create(df,output_path=p1)
    slide.shapes.add_picture(p1, 0.02*prs_info['prs_width'], 0.077*prs_info['prs_height'],height=0.85*prs_info['prs_height'])    
    pass

def fun_caller_point_list(slide, df, prs_info):
    left = 0.02
    top = 0.2
    step = min(0.8/df.shape[0], 0.1)

    for i,r in df.iterrows():
        slide_add_text(prs_info,slide,left=left+0.03,top=top, text_str=r['要点'], font_size=24, font_color=RGBColor(0, 0, 0), opacity=0.3,box_width=0.9,is_fill=False)

        # 添加正方形形状（这里使用矩形，因为python-pptx没有直接的正方形类型，但可以通过设置相同的宽度和高度来创建正方形）
        shape = slide.shapes.add_shape(
            MSO_SHAPE.RECTANGLE,  # 使用MSO_SHAPE枚举指定形状类型
            int(left*prs_info['prs_width']),
            int(top*prs_info['prs_height']),
            int(step*prs_info['prs_height']*0.6),
            int(step*prs_info['prs_height']*0.6)
        )
        fill = shape.fill
        fill.solid()
        # color = prs_info['theme_color'][i % len(prs_info['theme_color'])]
        fill.fore_color.rgb = prs_info['theme_color'][i % len(prs_info['theme_color'])]
        line = shape.line
        line.fill.background()  # 将线条填充设置为背景，这样线条就不会显

        top += step

def fun_caller_point_hexagon(slide, df, prs_info):

    start_x = 0.24
    start_y = 0.15
    p_list = []          # start_x-i*step_x, start_y+i*step_y
    for i in range(3):
        step_x = 0.128
        step_y = 1.7320508075688772*step_x
        p_i = [start_x-i*step_x, start_y+i*step_y]
        p_list.append(p_i)
    for i, r in df.iterrows():
        diff_x = int(i/3)
        j = int(i%3)
        p_j = p_list[j]

        [left, top] = p_j
        left += diff_x*step_x*2
        text = r['要点']
        for ext in 'jpg,png'.split(','):
            background_image_path = f'''templates/{prs_info['prj']}/imgs/{r['要点']}.{ext}'''
            if os.path.exists(background_image_path):
                break
        # 增加一个六边形
        temp_img_path = 'temp/point_hexagon.png'
        create_hexagon.create(background_image_path=background_image_path,text=r['要点'],output_path=temp_img_path,font_size=50)
        size_ratio = 0.28
        slide.shapes.add_picture(temp_img_path, left*prs_info['prs_height'], top*prs_info['prs_height'], width=size_ratio*prs_info['prs_height'], height=size_ratio*prs_info['prs_height'])

def fun_caller_urls(slide, df, prs_info):
    column_widths = {"链接":3}
    slide_add_table(prs_info, slide, df)

def fun_caller_screen_cut(slide, df, prs_info):
    p1 = f'''templates/{prs_info['prj']}/imgs/{df['单图片地址'][0]}'''
    slide.shapes.add_picture(p1, left=0.02*prs_info['prs_width'], top=0.08*prs_info['prs_height'],height=0.85*prs_info['prs_height'])  
    pass

class MyPrs():
    def __init__(self, prj='生生ESG介绍',theme='tableau',fill_color=RGBColor(0, 149, 162)):
        theme_color_dict = {
            'ss':[hex_to_rgbcolor(hex_color) for hex_color in '#FFD700,#ADD8E6,#F0E68C,#FFB6C1,#87CEEB,#98FB98,#FFA07A,#DDA0DD'.split(',')],
            'tableau':[hex_to_rgbcolor(hex_color) for hex_color in '#4e79a7,#f28e2c,#e15759,#76b7b2,#59a14f,#edc949,#af7aa1,#ff9da7,#9c755f,#bab0ab'.split(',')],
        }
        
        self.prj = prj
        self.fill_color=fill_color
        self.theme_color = theme_color_dict.get(theme)
        # print(self.theme_color)
        self.template_path=os.path.join(config.ROOT_PATH, f'templates/{prj}/模板.pptx')
        self.output_path = os.path.join(config.ROOT_PATH, f'templates/{prj}/output.pptx')
        print('结果保存在..............1    ', self.output_path)
        self.excel_path=os.path.join(config.ROOT_PATH, f'templates/{prj}/PAGES.xlsx')
        self.bg_path = os.path.join(config.ROOT_PATH, f'templates/{prj}/背景.png')
        self.prs = Presentation(self.template_path)
        self.templates_slide_count = len(self.prs.slides)
        self.sheet_names = pd.ExcelFile(self.excel_path).sheet_names
        self.prs_width = self.prs.slide_width
        self.prs_height = self.prs.slide_height
        # print('ppt的宽度和高度..........', self.prs_width, self.prs_height)

        type_dict = {}
        for i in self.sheet_names:
            type_i = i.split('-')[0]
            if type_i not in type_dict:
                type_dict[type_i] = ''

        # print('\n所有的sheet列表...........', self.sheet_names)
        # print('\n当前需要的主题.........', type_dict, '\n\n')

        # 根据sheet的类别找到对应的函数处理
        self.fun_caller_dict = fun_caller_dict  # 在pptx_type_caller中定义

    def slide_delete(self,delete_index=0):
        if len(self.prs.slides) > delete_index:
            # 获取第一张幻灯片的 rId
            to_delete = self.prs.slides._sldIdLst[delete_index].rId
            # 从演示文稿中删除第一张幻灯片
            del self.prs.slides._sldIdLst[delete_index]
            # 删除对应的关系
            self.prs.part.drop_rel(to_delete)

    def slide_add_new(self):
        # 插入一个表格
        slide_layout = self.prs.slide_layouts[5]
        slide = self.prs.slides.add_slide(slide_layout)    

        # 设置背景
        left = top = Inches(0)
        width = self.prs.slide_width
        height = self.prs.slide_height
        pic = slide.shapes.add_picture(self.bg_path, left, top, width, height)
        return slide

    def save(self):
        try:
            self.prs.save(self.output_path)
        except Exception as e:
            print(e)
        try:
            print('结果保存在..............2    ', f'temp/{self.prj}.pptx')
            self.prs.save(f'temp/{self.prj}.pptx')
        except Exception as e:
            print(e)

    def create_one_sheet(self, sheet_i):
        slide = self.slide_add_new()
        type_i = sheet_i.split('-')[0]
        text_i = sheet_i.split('-')[1]
        slide_add_text(self.__dict__, slide, text_str=text_i, font_color=RGBColor(255, 255, 255),font_bold=True)  # 插入标题

        df = pd.read_excel(self.excel_path, sheet_name=sheet_i).fillna('')

        # 根据type_i调用对应的处理函数
        func_name = self.fun_caller_dict.get(type_i)
        if func_name and func_name in globals():
            # 从全局作用域中获取函数引用
            func = globals()[func_name]
            func(slide, df, prs_info=self.__dict__)  # 调用函数
        else:
            print(f"未找到函数: {func_name}")

    def create_all_sheet(self):
        for sheet_i in self.sheet_names:
            self.create_one_sheet(sheet_i)

        slide = self.slide_add_new()
        p1 = os.path.join(config.ROOT_PATH, 'static/imgs/小船.png')
        # slide.shapes.add_picture(p1, 0.4*self.prs_width, 0.4*self.prs_height)
        slide_add_text(self.__dict__,slide,left=0.32,top=0.35, text_str='THANKS', font_size=80,font_bold=True, font_color=self.fill_color, opacity=0.3,box_width=0.5,is_fill=False)        
        pass

    def run(self):
        self.create_all_sheet()
        self.save()
        pass


if __name__=='__main__':
    MyPrs(prj='生生ESG介绍').run()

